using System.Diagnostics;

namespace SharpTalk.Core.Data.SqLiteDataProvider
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Diagnostic;
    using Enums;
    using Services;

    /// <summary>
    /// 
    /// </summary>
    public sealed class SqLiteChatRoomDataProvider
         : SqLiteDataProviderBase<ChatRoom>, IChatRoomDataProvider
    {

        public SqLiteChatRoomDataProvider(string filepath)
            : base(filepath)
        {
        }

        public SqLiteChatRoomDataProvider(string filepath, ITraceProvider traceProvider) 
            : base(filepath, traceProvider)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        public override void Initialize()
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            base.Initialize();
            
            var room = Find(Guid.Empty);
            if (room == null)
                Save(ChatRoomService.GetDefaultChatRoom());
        }
        /// <summary>
        /// Deletes all data
        /// </summary>
        public override void ClearAll()
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_DELETE_ALL;
                    cmd.ExecuteNonQuery();

                    cmd.CommandText = CMD_CHATROOMUSERS_DELETE_ALL;
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(),exp);
            }

        }
        /// <summary/>
        /// <param name="obj"/>
        /// <returns/>
        public override bool Save(ChatRoom obj)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            var success = false;
            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_CREATE;
                    cmd.Parameters.AddWithValue(null, obj.Guid);
                    cmd.Parameters.AddWithValue(null, (byte)obj.Type);
                    cmd.Parameters.AddWithValue(null, obj.Owner);
                    cmd.Parameters.AddWithValue(null, obj.Name);
                    cmd.Parameters.AddWithValue(null, obj.Image);
                    cmd.Parameters.AddWithValue(null, obj.PasswordHash);
                    cmd.Parameters.AddWithValue(null, obj.IsDefault);
                    cmd.Parameters.AddWithValue(null, DateTime.Now);
                    cmd.Parameters.AddWithValue(null, obj.Motd);

                    cmd.ExecuteNonQuery();
                }
                success = true;
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
            }

            return success;
        }
        /// <summary/>
        /// <returns/>
        public override List<ChatRoom> All()
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            var chatrooms = new List<ChatRoom>();
            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_READ_ALL;
                    using(var reader = cmd.ExecuteReader())
                    {
                        if(reader.HasRows)
                            while (reader.Read())
                            {
                                chatrooms.Add( new ChatRoom()
                                                   {
                                                       Guid = reader.GetGuid(0),
                                                       Type = (ChatRoomTypeEnum)reader.GetByte(1),
                                                       Owner = reader.GetGuid(2),
                                                       Name = reader.GetString(3),
                                                       Image = reader.IsDBNull(4) ? null : reader.GetValue(4) as byte[],
                                                       PasswordHash = reader.IsDBNull(5) ? null : reader.GetValue(5) as byte[],
                                                       IsDefault = reader.GetBoolean(6),
                                                       Motd  = reader.IsDBNull(7) ? null : reader.GetString(7)
                                                   });
                            }
                    }

                    // Get Chatroom's users
                    cmd.CommandText = CMD_CHATROOMUSERS_READ_CHATROOM;
                    cmd.Parameters.AddWithValue(null, null);
                    foreach (var chatRoom in chatrooms)
                    {
                        cmd.Parameters[0].Value = chatRoom.Guid;

                        using(var reader = cmd.ExecuteReader())
                        {
                            if(!reader.HasRows) continue;
                            while (reader.Read())
                            {
                                chatRoom.AddUser(reader.GetGuid(1));
                            }
                        }
                    }
                }
              
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
            }

            return chatrooms;
        }
        /// <summary/>
        /// <param name="obj"/>
        /// <returns/>
        public override ChatRoom Update(ChatRoom obj)
        {
            return Update(obj, true);
        }

        /// <summary/>
        /// <param name="obj"/>
        /// <param name="definePassword"></param>
        /// <returns/>
        public ChatRoom Update(ChatRoom obj, bool definePassword)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            
            try
            {
                using (var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText =  definePassword ? CMD_CHATROOMS_UPDATE : CMD_CHATROOMS_UPDATE_NO_PASSWORD;
                    if (definePassword) cmd.Parameters.AddWithValue(null, obj.PasswordHash);
                    cmd.Parameters.AddWithValue(null, obj.Image);
                    cmd.Parameters.AddWithValue(null, DateTime.Now);
                    cmd.Parameters.AddWithValue(null, obj.Motd);
                    cmd.Parameters.AddWithValue(null, (byte)obj.Type);
                    cmd.Parameters.AddWithValue(null, obj.Guid);

                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
                return null;
            }
            return obj;
        }
        /// <summary/>
        /// <param name="obj"/>
        /// <returns/>
        public override bool Delete(ChatRoom obj)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            return obj != null ? Delete(obj.Guid) : false;
        }
        /// <summary/>
        /// <param name="guid"/>
        /// <returns/>
        public override bool Delete(Guid guid)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            var success = false;
            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.Parameters.AddWithValue(null, guid);

                    cmd.CommandText = CMD_CHATROOMS_DELETE_GUID;
                    cmd.ExecuteNonQuery();

                    cmd.CommandText = CMD_CHATROOMUSERS_DELETE_CHATROOM;
                    cmd.ExecuteNonQuery();
                }
                success = true;
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
            }
            return success;
        }
        /// <summary/>
        /// <param name="guid"/>
        /// <returns/>
        public override ChatRoom Find(Guid guid)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            ChatRoom result = null;
            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_READ_GUID;
                    cmd.Parameters.AddWithValue(null, guid);
                    using(var reader = cmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return result;

                        reader.Read();
                        result = new ChatRoom()
                                           {
                                               Guid = reader.GetGuid(0),
                                               Type = (ChatRoomTypeEnum) reader.GetByte(1),
                                               Owner = reader.GetGuid(2),
                                               Name = reader.GetString(3),
                                               Image = reader.IsDBNull(4) ? null : reader.GetValue(4) as byte[],
                                               PasswordHash = reader.IsDBNull(5) ? null : reader.GetValue(5) as byte[],
                                               IsDefault = reader.GetBoolean(6),
                                               Motd = reader.IsDBNull(7) ? null : reader.GetString(7)
                                           };
                    }

                    // Get Chatroom's users
                    cmd.CommandText = CMD_CHATROOMUSERS_READ_CHATROOM;
                    using(var reader = cmd.ExecuteReader())
                    {
                        if(!reader.HasRows) return result;
                        while (reader.Read())
                        {
                            result.AddUser(reader.GetGuid(1));
                        }
                    }

                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
            }

            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public List<ChatRoom> Find(ChatRoomTypeEnum type)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            var chatrooms = new List<ChatRoom>();

            try
            {

                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_READ_TYPE;
                    cmd.Parameters.AddWithValue(null, (byte)type);
                    using(var reader = cmd.ExecuteReader())
                    {
                        if(!reader.HasRows) return chatrooms;
                        while (reader.Read())
                        {
                            chatrooms.Add(new ChatRoom()
                                              {
                                                  Guid = reader.GetGuid(0),
                                                  Type = (ChatRoomTypeEnum)reader.GetByte(1),
                                                  Owner = reader.GetGuid(2),
                                                  Name = reader.GetString(3),
                                                  Image = reader.IsDBNull(4) ? null : reader.GetValue(4) as byte[],
                                                  PasswordHash = reader.IsDBNull(5) ? null : reader.GetValue(5) as byte[],
                                                  IsDefault = reader.GetBoolean(6),
                                                  Motd = reader.IsDBNull(7) ? null : reader.GetString(7)
                                              });
                        }
                    }


                    // Get chatrooms users
                    cmd.CommandText = CMD_CHATROOMUSERS_READ_CHATROOM;
                    cmd.Parameters.Clear();
                    cmd.Parameters.AddWithValue(null, null);
                    foreach (var chatRoom in chatrooms)
                    {
                        cmd.Parameters[0].Value = chatRoom.Guid;
                        using (var reader = cmd.ExecuteReader())
                        {
                            if (!reader.HasRows) continue;
                            while (reader.Read())
                            {
                                chatRoom.AddUser(reader.GetGuid(1));
                            }
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
            }
            return chatrooms;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ChatRoom Find(string name)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            ChatRoom chatRoom = null;
            try
            {
                using (var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_READ_NAME;
                    cmd.Parameters.AddWithValue(null, name);
                    using (var reader = cmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return chatRoom;

                        reader.Read();
                        chatRoom = new ChatRoom()
                                       {
                                           Guid = reader.GetGuid(0),
                                           Type = (ChatRoomTypeEnum)reader.GetByte(1),
                                           Owner = reader.GetGuid(2),
                                           Name = reader.GetString(3),
                                           Image = reader.IsDBNull(4) ? null : reader.GetValue(4) as byte[],
                                           PasswordHash = reader.IsDBNull(5) ? null : reader.GetValue(5) as byte[],
                                           IsDefault = reader.GetBoolean(6),
                                           Motd = reader.IsDBNull(7) ? null : reader.GetString(7)
                                       };
                    }

                    // Get Chatroom's users
                    cmd.CommandText = CMD_CHATROOMUSERS_READ_CHATROOM;
                    cmd.Parameters.Clear();
                    cmd.Parameters.AddWithValue(null, chatRoom.Guid);
                    using (var reader = cmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return chatRoom;
                        while (reader.Read())
                        {
                            chatRoom.AddUser(reader.GetGuid(1));
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
            }
            return chatRoom;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public ChatRoom Find(string name, ChatRoomTypeEnum type)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            ChatRoom chatRoom = null;
            try
            {
                using (var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMS_READ_TYPE_NAME;
                    cmd.Parameters.AddWithValue(null, (byte)type);
                    cmd.Parameters.AddWithValue(null, name);
                    using (var reader = cmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return chatRoom;

                        reader.Read();
                        chatRoom = new ChatRoom()
                                       {
                                           Guid = reader.GetGuid(0),
                                           Type = (ChatRoomTypeEnum)reader.GetByte(1),
                                           Owner = reader.GetGuid(2),
                                           Name = reader.GetString(3),
                                           Image = reader.IsDBNull(4) ? null : reader.GetValue(4) as byte[],
                                           PasswordHash = reader.IsDBNull(5) ? null : reader.GetValue(5) as byte[],
                                           IsDefault = reader.GetBoolean(6),
                                           Motd = reader.IsDBNull(7) ? null : reader.GetString(7)
                                       };
                    }

                    // Get Chatroom's users
                    cmd.CommandText = CMD_CHATROOMUSERS_READ_CHATROOM;
                    cmd.Parameters.Clear();
                    cmd.Parameters.AddWithValue(null, chatRoom.Guid);
                    using (var reader = cmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return chatRoom;
                        while (reader.Read())
                        {
                            chatRoom.AddUser(reader.GetGuid(1));
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp, true);
            }
            return chatRoom;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="chatroom"></param>
        /// <param name="user"></param>
        public bool Join(Guid chatroom, Guid user)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();

            var success = false;
            try
            {
                using(var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMUSERS_CREATE;
                    cmd.Parameters.AddWithValue(null, chatroom);
                    cmd.Parameters.AddWithValue(null, user);
                    cmd.ExecuteNonQuery();
                }
                success = true;
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
            }
            return success;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="chatroom"></param>
        /// <param name="user"></param>
        public void Leave(Guid chatroom, Guid user)
        {
            if (ShouldTrace(TraceEventType.Verbose))TraceMethodCall(MethodBase.GetCurrentMethod());
            this.ThrowIfDisposed();
            try
            {
                using (var cmd = Connection.CreateCommand())
                {
                    cmd.CommandText = CMD_CHATROOMUSERS_DELETE;
                    cmd.Parameters.AddWithValue(null, chatroom);
                    cmd.Parameters.AddWithValue(null, user);
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception exp)
            {
                TraceException(MethodBase.GetCurrentMethod(), exp);
            }
        }
        
    }
}